/*
 * Copyright 2010 Katsunori Koyanagi
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package sweezy.anim.easing
{
	import sweezy.anim.core.IPhisycalEasing;

	public class PhisycalEasing
	{

		public static const DEFAULT_FRAME_RATE:Number = 24;

		private static var _frameRate:Number = 24;

		public static function accelerate(initialVelocity:Number = NaN, acceleration:Number = NaN, frameRate:Number = NaN):IPhisycalEasing
		{
			if (isNaN(initialVelocity))
			{
				initialVelocity = 0;
			}
			if (isNaN(acceleration) || acceleration <= 0)
			{
				acceleration = 1;
			}
			if (isNaN(frameRate) || frameRate <= 0)
			{
				frameRate = DEFAULT_FRAME_RATE;
			}

			return new AccelerateEasing(initialVelocity, acceleration, frameRate);
		}

		public static function decelerate(factor:Number = NaN, threshold:Number = NaN, frameRate:Number = NaN):IPhisycalEasing
		{
			if (isNaN(factor) || factor <= 0)
			{
				factor = 0.2;
			}
			if (isNaN(threshold) || threshold <= 0)
			{
				threshold = 0.05;
			}
			if (isNaN(frameRate) || frameRate <= 0)
			{
				frameRate = DEFAULT_FRAME_RATE;
			}

			return new DecelerateEasing(factor, threshold, frameRate);
		}

		public static function get frameRate():Number
		{
			return _frameRate;
		}

		public static function set frameRate(value:Number):void
		{
			if (isNaN(value) || value <= 0)
			{
				value = DEFAULT_FRAME_RATE;
			}

			_frameRate = value;
		}

		public static function uniform(velocity:Number = NaN, frameRate:Number = NaN):IPhisycalEasing
		{
			if (isNaN(velocity) || velocity <= 0)
			{
				velocity = 1;
			}
			if (isNaN(frameRate) || frameRate <= 0)
			{
				frameRate = DEFAULT_FRAME_RATE;
			}

			return new UniformEasing(velocity, frameRate);
		}
	}
}

import sweezy.anim.core.IPhisycalEasing;

class DecelerateEasing implements IPhisycalEasing
{

	private var _factor:Number;

	private var _frameRate:Number;

	private var _threshold:Number;

	public function DecelerateEasing(factor:Number, threshold:Number, frameRate:Number)
	{
		_factor = factor;
		_threshold = threshold;
		_frameRate = frameRate;
	}

	public function computeDuration(distance:Number):uint
	{
		distance = distance < 0 ? -distance : distance;
		return (Math.log(_threshold / distance) / Math.log(1 - _factor) + 1) * _frameRate;
		
	}

	public function ease(fraction:Number):Number
	{
		return Math.pow(1 - _factor, 1 / fraction - 1);
	}
}


class AccelerateEasing implements IPhisycalEasing
{

	private var _acceleration:Number;

	private var _frameRate:Number;

	private var _initialVelocity:Number;

	public function AccelerateEasing(initialVelocity:Number, acceleration:Number, frameRate:Number)
	{
		_initialVelocity = initialVelocity;
		_acceleration = acceleration;
		_frameRate = frameRate;
	}

	public function computeDuration(distance:Number):uint
	{
		distance = distance < 0 ? -distance : distance;
		return (Math.sqrt(_initialVelocity * _initialVelocity + 2 * _acceleration * distance) - _initialVelocity) / _acceleration * _frameRate;
	}

	public function ease(fraction:Number):Number
	{
		return (_initialVelocity * fraction + fraction * fraction * _acceleration / 2) / (_initialVelocity + _acceleration / 2);
	}
}

class UniformEasing implements IPhisycalEasing
{

	private var _frameRate:Number;

	private var _velocity:Number;

	public function UniformEasing(velocity:Number, frameRate:Number)
	{
		_velocity = velocity;
		_frameRate = frameRate;
	}

	public function computeDuration(distance:Number):uint
	{
		distance = distance < 0 ? -distance : distance;
		return distance / _velocity * _frameRate;
	}

	public function ease(fraction:Number):Number
	{
		return fraction;
	}
}
